NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name [CCP] Billing & Inventory Services for Printavo & Online Labels v2
// @namespace CCP
// @include https://www.printavo.com/*
// @include https://secure.onlinelabels.com/Account/OrderHistory.aspx
// @include https://www.onlinelabels.com/products/*
// @require http://code.jquery.com/jquery-3.4.1.min.js
// @resource customCSS https://fitaf570.com/jqui/ui-lightness.css
// @require https://code.jquery.com/ui/1.12.0/jquery-ui.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/arrive/2.4.1/arrive.min.js
// @downloadURL https://openuserjs.org/install/macheteYeti/[CCP]_Billing_Inventory_Services_for_Printavo_Online_Labels_v2.user.js
// @updateURL https://openuserjs.org/install/macheteYeti/[CCP]_Billing_Inventory_Services_for_Printavo_Online_Labels_v2.user.js
// @grant GM_addStyle
// @grant GM_getResourceText
// @grant GM_getValue
// @grant GM_setValue
// @license MIT
// @author macheteYeti
// @description Adds functionality to Printavo & OnlineLabels to enable stock management features.
// @version 40
// ==/UserScript==
$(document).ready(function(){
user=localStorage.getItem('CCPuser');
shop=localStorage.getItem('CCPshop');
//determine whether using Online Labels or Printav
var plat='ol';
if(window.location.href.indexOf("printavo")>-1)plat='pa';
//specify statuses which should trigger an inventory update
var include=['Ready for Pickup','Delivery','Ship','In Production'];
//serve different code based on which platform is in use
switch(plat){
case 'ol':
if(user=='reception')return false;
if(window.location.href.indexOf('products')>-1){
//if on a product page, determine if we've passed ordering parameters
var urlParams = new URLSearchParams(window.location.search);
if(typeof urlParams.get('qty')!=typeof undefined&&urlParams.get('qty')!=null&&urlParams.get('qty').length>0){
//fill in order quantty
$('.v-pob__custom-quantity-input').val(urlParams.get('qty'));
setTimeout(()=>{
//add to cart
$('.v-pob__add-to-cart')[0].click();
setTimeout(()=>{
//if this is the last item to order, go to cart, otherwise close the page
if(urlParams.get('last')==null)window.close();
else window.location.href='https://secure.onlinelabels.com/Cart/ViewCart.aspx';
},1500);
},1500);
}
}
else{
//if we're not on a product page, we know we're on the order history page due to distinct @includes
//gather up visible invoices
var foundInvoices=[];
$('a[href*=OrderDetails]').each(function(){ foundInvoices.push($(this).attr('href').split('?')[1]); });
//check if we've tracked visible invoices
$.ajax({type:'GET',url:'https://fitaf570.com/ccp/getStock.php',async:false,dataType:'json',data:{type:'invoices',foundInvoices:foundInvoices},fail:function(jqXHR,textStatus,errorThrown){console.log('Could not get invoices, server response: '+textStatus+': '+errorThrown);},success:function(d){
var tracked='<a class="c-button c-button--secondary c-button--full u-margin-bottom--tiny inventoryTracking tracked">Inventory Tracked!</a>';
var untracked='<a class="ss-duplicate-order c-button c-button--full u-margin-bottom--tiny inventoryTracking" style="">Track Inventory (Now)</a><a class="ss-duplicate-order c-button c-button--full u-margin-bottom--tiny inventoryTracking threeDays">Track Inventory<br>(In 3 Days)</a>';
$('.p-order-box').each(function(){
//for each order, based on if we find the invoice ID, put out the relevant buttons
if($.inArray($(this).find('a[href*=OrderDetails]').attr('href').split('?')[1],d.inv)>-1)$(tracked).prependTo($(this).find('.p-order-box__totals>div').first());
else $(untracked).prependTo($(this).find('.p-order-box__totals>div').first());
})
}});
$(document).on('click','.inventoryTracking:not(.tracked)',function(){
//when clicking an inventory button, gather the details
var orig=$(this);
var data={'type':'stockChange','action':'add','inv':$(this).parents('.p-order-box').find('a[href*=OrderDetails]').attr('href').split('?')[1]};
if($(this).hasClass('threeDays'))data['when']='interval';
else data['when']='now';
//gather the products
$(this).parents('.p-order-box').find('.u-border--top.u-large-text-align--left').find('.row.align-middle').each(function(){
vals=[$(this).find('div').first().next().find('div').first().next().contents().text().trim().trimLeft()];
$.each($(this).find('div').first().next().text().replaceAll("\n\n","\n").split("\n"),function(i,v){
if(v.indexOf('Sheets')>-1)vals.push(v.trim().trimLeft().replace("Sheets","").trim().trimLeft());
});
data['vals']=vals;
//send the data
$.ajax({type:'GET',url:'https://fitaf570.com/ccp/getStock.php',async:false,dataType:'json',data:data});
});
//hide the recording buttons
$(orig).parent().find('.inventoryTracking').fadeOut();
});
}
break;
case 'pa':
//if we don't already have a generic inventory utilization button, add it to the top nav
if($('#genericRecord').length==0)$('.nav.navbar-nav').prepend('<li><a class="menu-item" title="Record Supply Usage" id="genericRecord"><img width="16" height="16" class="new-item__icon" src="https://djqnomyzwkbyb.cloudfront.net/assets/app/icon-new@2x-074c3957e49b442a12334ec6132993212f3e9a0241a52251f6b9832dc17b2e62.png">Record</a></li>');
//retrieve the products we will use in the autocomplete lookup
sku=$.ajax({type:'GET',url:'https://fitaf570.com/ccp/getStock.php',async:false,dataType:'json',data:{type:'sku'},fail:function(jqXHR,textStatus,errorThrown){console.log('Could not get sku, server response: '+textStatus+': '+errorThrown);}}).responseJSON;
//instantiate the popup to gather the inventory utilization
$('body').append('<div id="inventoryDialog" style="display:none" title="Log Usage"><form autocomplete="off"><input id="inventoryQty" placeholder="Sheets used" autocomplete="off"><br><input id="inventoryLookup" placeholder="Select a Stock"><input type="hidden" id="inventoryJob"></form></div>');
$('#inventoryDialog').dialog({autoOpen:false,width:300,position:{my:'right center',at:'right bottom',of: event}});
//hide the popup initially
$('#inventoryDialog').dialog('close');
function openDialog(loc=1,e){
/*
loc 1 = calendar
loc 2 = generic top nav record
loc 3 = forced generic top nav record on invoice page load
*/
switch(loc){
case 1:
$('.jq-order-preview-modal').css('display','block');
$('#inventoryDialog').dialog({appendTo:'.jq-order-preview-modal'})
//set job via url in Printavo popup
$('#inventoryJob').val($('.modal-content.modal-calendar .btn.btn-success.pull-left').attr('href').split('/')[2]);
break;
case 2:
//set job via url
if(window.location.href.indexOf('/invoices/')>-1)$('#inventoryJob').val(window.location.href.split('/').pop());
$('#inventoryDialog').dialog('open');
break;
case 3:
//set job via url
$('#inventoryJob').val(window.location.href.split('/').pop());
break;
}
$('#inventoryDialog').dialog("option","position",{my: "right bottom+15", of: e});
// if(user!='reception'&&shop!='kingston')$('#inventoryDialog').dialog('open');
$('#inventoryQty').focus();
}
//if someone tries to look up a product before specifying a quantity, we add error styles to the quantity box. when someone starts typing in it, they are removed
$(document).on('keydown','#inventoryQty.ui-state-error',function(){ $(this).removeClass('ui-state-error'); });
$('#inventoryLookup').autocomplete({
source:sku,
width:200,
delay:100,
autoFocus:true,
focus:function(){$(this).focus();},
select:function(event,ui){
//when we select a stock from the autocomplete list, check that we have specified a quantity
if($('#inventoryQty').val().length==0){
$('#inventoryQty').addClass('ui-state-error').effect('shake');
$('#inventoryLookup').val('');
$('#inventoryQty').focus();
return false;
}
//if a quantity has been specified, gather the rest of the info
var dat={type:'stockChange',action:'subtract',val:$('#inventoryQty').val(),id:ui.item.value,user:user,inv:$('#inventoryJob').val()};
// $('#inventoryLookup').val(ui.item.label);
//send the record
$.ajax({type:'GET',url:'https://fitaf570.com/ccp/getStock.php',async:false,dataType:'json',data:dat,success:function(d){
//once recorded, clear the popup and ready it for another input
$('#inventoryDialog form')[0].reset();
$('#inventoryLookup').val('').empty();
$('#inventoryQty').focus();
}});
this.value = "";
return false;
}
});
if(window.location.href.indexOf('/invoices/')>-1||window.location.href.indexOf('/customers/')>-1){
if(window.location.href.indexOf('/invoices/')>-1){
//if we're on an invoice page, store the hashed work order URL
var wo='';
$.each($('.dropdown-menu.pull-right a'),function(i,v){
if($(this).attr('href').indexOf('work_orders')>-1)wo=$(this).attr('href').split('/').pop();
});
$.ajax({type:'GET',url:'https://fitaf570.com/ccp/getStock.php',async:false,dataType:'json',data:{type:'wo',pid:window.location.href.split('/').pop(),wo:wo}});
}
//if we're on an invoice page, check if we have recorded inventory usage for it
//if not show the popup
$.ajax({type:'GET',url:'https://fitaf570.com/ccp/getStock.php',async:false,dataType:'json',data:{type:'checkInvoice',inv:window.location.href.split('/').pop()},success:function(d){
if(!d.inv&&window.location.href.indexOf('/invoices/')>-1)openDialog(3,$('#genericRecord'));
}});
async function setPaymentIntent(){
//readLoc 1 = individual invoice 2 = all unbilled on /customers/ 3 = partial payment on /invoices/ 4 = partial payment on /customers/
fetchURL="https://fitaf570.com/ccp/stripe2.php?";
params={'type':'getIntent','invoices':[],ids:[],'due':0,'user':user,'shop':shop,readLoc:readLoc};
if(readLoc==1){
var due=1.0*($('.ref-invoice-wrapper.orders-invoice-wrapper.invoice-layout .well.well__no-border').first().find('.form-control.form-control__disabled-bare').last().text().trim().trimLeft().replace('$','').replaceAll(',','')*100);
if(due==0||due==0.0){
let due=prompt("Could not get invoice total. Please input it here.");
}
params.dues=[due];
params.due+=due;
params.invoices.push($('h3.pdf-invoice-number').text().split('#')[1].trim());
params.ids.push(window.location.href.split('/').pop());
}
else if(readLoc==3){
params.due=prompt("Enter Partial Payment Amount");
params.due*=100;
params.invoices.push($('h3.pdf-invoice-number').text().split('#')[1].trim());
params.ids.push(window.location.href.split('/').pop());
}
else if(readLoc==2){
params.dues=[];
$('#orders-index-list tr').each(function(){
if($(this).find('.paid-label').length>0)return true;
if($.inArray($(this).find('.status').last().text().trim(),noBillStati)==-1){
var thisTot=$(this).find('td').last().prev().find('.text-muted').text().replace('$','').replaceAll(',','')*100;
params.due+=1.0*thisTot;
params.dues.push(thisTot);
params.ids.push($(this).attr('data-clickable').split('/').pop());
params.invoices.push($(this).find('td').first().clone().children().remove().end().text().trim().trimLeft());
}
});
console.log(params);
}
else if(readLoc==4){
params.dues=[];
$('#new_payment tbody tr').each(function(){
if($(this).find('input').val().length>1){
var thisTot=$(this).find('input').val()*100;
params.due+=1.0*thisTot;
params.dues.push(thisTot);
params.ids.push($(this).find('td').first().find('a').attr('href').split('/').pop());
params.invoices.push($(this).find('td').first().find('a').text().replace('#',''));
}
});
console.log(params);
}
$('body').removeClass('loading');
await fetch(fetchURL+$.param(params),{method:"POST"}).then((response)=>{
if(response.status>=400&&response.status<600){
alert("Stripe servers could not be reached. Please restart the card reader. If the problem persists, contact Yeti");
}
return response;
}).catch((error)=>{
console.log(error);
});
}
function unexpectedDisconnect(){
//should an auto-reconnect be built here?
alert('Lost connection to card reader. Refresh the page and try again.');
$('body').removeClass('loading');
}
$('<li class="dropdown-item item"><a class="serveInvoice" data-type="single"><span class="dollar">$</span> Feed Card Reader</a></li><li class="dropdown-item item"><a class="serveInvoice" data-type="partial"><span class="dollar">$</span> Feed Card Reader (Partial)</a></li><li class="divider"></li>').insertAfter($('.invoice__sidebar-ul .dropdown-menu .divider').eq(($('.invoice__sidebar-ul .dropdown-menu .divider').length-4)));
if($('.paid-label').length&&window.location.href.indexOf('/invoices/')>-1)$('.serveInvoice').addClass('disabled');
$(document).on('click','.serveInvoice',function(e){
if($(this).hasClass('disabled')){
alert("It appears this invoice is already paid. If you disagree, contact Yeti and provide an invoice ID.");
return false;
}
$('body').addClass('loading');
console.log('feeding reader based on loc');
e.preventDefault();
if($(this).attr('data-type')=='single')readLoc=1;
else if($(this).attr('id')=='feedSelected')readLoc=4;
else if($(this).attr('data-type')=='partial')readLoc=3;
else readLoc=2;
setPaymentIntent();
});
if(window.location.href.indexOf('/customers/')>-1){
noBillStati=['Completed','Loss'];
$('.modal-footer:not(.readered)').append($('<input value="Feed Card Reader (Selected)" class="btn btn-success pull-right serveInvoice" data-type="multi" id="feedSelected">'));
$('.modal-footer').addClass('readered');
$(document).arrive('#new_payment',function(){
$('.modal-footer:not(.readered)').append($('<input value="Feed Card Reader (Selected)" class="btn btn-success pull-right serveInvoice" data-type="multi" id="feedSelected">'));
$('.modal-footer').addClass('readered');
});
$('<li><a href="#" class="serveInvoice" data-type="multi">Feed Card Reader (<span id="selectedCount">All</span> Unpaid)</a> <span id="selectedTot"></span></li>').insertAfter($('.sidebar-menu li').last());
}
}
$(document).on('click','.dropdown-select-status__menu li',function(e){
//when we change status on the calendar, if we are specifying a qualified status, show the popup
if($.inArray($(this).text().trim().trimLeft(),include)>-1)openDialog(1,e);
});
$(document).on('click','#genericRecord',function(e){
//if we click the generic record button in the top nav, show the popup
openDialog(2,e);
});
break;
default:break;
}
});
var newCSS = GM_getResourceText ("customCSS");
GM_addStyle (newCSS);
GM_addStyle( `
.inventoryTracking{position:absolute;top:0;left:0;width:27%;margin-left:3%}
.tracked{cursor:default}
.threeDays{left:30%}
#inventoryDialog{width:200px;height:200px}
.ui-dialog.ui-corner-all.ui-widget.ui-widget-content{z-index:999}
#inventoryDialog input{width:150px;margin-bottom:1em}
#inventoryQty.ui-state-error::placeholder{color:#fff}
#genericRecord{cursor:pointer}
.dollar{font:1.4em 'calibri',sans-serif;font-weight:bold;color:#ADB9C0;display:inline-block;margin-right:8px}
.serveInvoice{cursor:wait}
div[aria-describedby='inventoryDialog']{position:fixed !important}
.checker,.serveInvoice{cursor:pointer}
body.loading{cursor:progress}
body.loading .serveInvoice{cursor:progress !important}
.serveInvoice.disabled{cursor:not-allowed !important}
#feedSelected{width:250px}
` );